/**
 *  EKology - An Ecosystem Simulation Framework
 *  Copyright (C) 2008  Aarón Tavío - aaron.tavio at gmail.com - ULPGC
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

package net.ekology.ekosystem.datatypes;

import net.ekology.core.datatypes.Automaton;

import java.util.Hashtable;
import java.io.*;

import org.w3c.dom.*;
import org.apache.xerces.dom.*;
import org.apache.xerces.parsers.*;
import org.apache.xml.serialize.*;
import org.xml.sax.InputSource;


/**
 * @author Aarón Tavío - aaron.tavio at gmail.com
 * @version 1.0.0 - 20081019-1700
 */
public class Genome implements Serializable {
    private static final long serialVersionUID = 53834983484721921L;
    
    protected String sID;
    protected String sSpeciesID;
    protected String sVersion;
    protected Hashtable<String,Gene> htGene;
    
    
    
    public Genome() {
        sID = null;
        sSpeciesID = null;
        sVersion = null;
        
        htGene = new Hashtable<String,Gene>();
    }
    
    public Genome(Genome oGenome) {
        this.sID = oGenome.getID();
        this.sSpeciesID = oGenome.getSpeciesID();
        this.sVersion = oGenome.getVersion();
        
        this.htGene = new Hashtable<String,Gene>();
        for(Gene oGene: oGenome.getGenes()) {
            this.htGene.put(oGene.sID, oGene);
        }
    }
    
    public Genome(String sID, String sSpeciesID, String sVersion) {
        this.sID = sID;
        this.sSpeciesID = sSpeciesID;
        this.sVersion = sVersion;
        
        htGene = new Hashtable<String,Gene>();
    }
    
    
    public Hashtable<String,String> getFeatures() {
        Hashtable<String,String> htResult = new Hashtable<String,String>();
        
        for(Gene oGene: htGene.values()) {
            if (oGene instanceof Feature) htResult.put(Feature.class.cast(oGene).sName, Feature.class.cast(oGene).sValue);
        }
        
        return htResult;
    }
    
    
    public String getFeature(String sFeature) {
        String sResult = null;
        
        for(Gene oGene: htGene.values()) {
            if (oGene instanceof Feature && Feature.class.cast(oGene).sName.equals(sFeature)) {
                sResult = Feature.class.cast(oGene).sValue;
                break;
            }
        }
        
        return sResult;
    }
    
    
    public Automaton getAutomaton() {
        Automaton oResult = new Automaton();
        
        for(Gene oGene: this.htGene.values()) {
            if (!(oGene instanceof Feature)) {
                if (oGene instanceof InitialState) {
                    String sName = InitialState.class.cast(oGene).sName;
                    
                    oResult.addState(new net.ekology.core.datatypes.InitialState(sName));
                    oResult.setInitialState(sName);
                    oResult.setCurrentStateName(sName);
                }
                else if (oGene instanceof FinalState) {
                    oResult.addState(new net.ekology.core.datatypes.FinalState(FinalState.class.cast(oGene).sName));
                }
                else if (oGene instanceof State) oResult.addState(new net.ekology.core.datatypes.State(State.class.cast(oGene).sName));
                else if (oGene instanceof TransitionRule) oResult.addTransitionRule(new net.ekology.core.datatypes.TransitionRule(TransitionRule.class.cast(oGene).sSourceState, TransitionRule.class.cast(oGene).sFiringEvent, TransitionRule.class.cast(oGene).sTargetState));
            }
        }
        
        return oResult;
    }
    
    
    public void buildFromFile(InputStream isInput) {
        Document oDocument;
        DOMParser oParser;
        Element oRootElem,oElem;
        NodeList oNodes;
        String sID,sType,sName, sValue,sCurrentState,sFiringEvent,sNextState;
        int iScore;
        Gene oGene;
        
        oParser = new DOMParser();
        try {
            oParser.parse(new InputSource(isInput));
            oDocument = oParser.getDocument();
            oRootElem = oDocument.getDocumentElement();
            
            this.sID = oRootElem.getAttribute("agentID");
            this.sSpeciesID = oRootElem.getAttribute("speciesID");
            this.sVersion = oRootElem.getAttribute("version");
            
            oNodes = oRootElem.getElementsByTagName("gene");
            for(int i=0; i<oNodes.getLength(); i++) {
                oElem = (Element) oNodes.item(i);
                oGene = null;
                
                sID = oElem.getAttributeNode("id").getValue();
                iScore = Integer.parseInt(oElem.getAttributeNode("score").getValue());
                sType = oElem.getAttributeNode("type").getValue();
                
                if (sType.equals("feature")) {
                    sName = oElem.getElementsByTagName("name").item(0).getTextContent();
                    sValue = oElem.getElementsByTagName("value").item(0).getTextContent();
                    
                    oGene = new Feature(sID,iScore, sName, sValue);
                }
                else if (sType.equals("initialState") || sType.equals("finalState")) {
                    sName = oElem.getElementsByTagName("name").item(0).getTextContent();
                    
                    oGene = (sType.equals("initialState"))? new InitialState(sID,iScore, sName) : new FinalState(sID,iScore, sName) ;
                }
                else if (sType.equals("rule")) {
                    sCurrentState = oElem.getElementsByTagName("currentState").item(0).getTextContent();
                    sFiringEvent = oElem.getElementsByTagName("firingEvent").item(0).getTextContent();
                    sNextState = oElem.getElementsByTagName("nextState").item(0).getTextContent();
                    
                    oGene = new TransitionRule(sID,iScore, sCurrentState, sFiringEvent, sNextState);
                }
                    
                if (oGene != null) htGene.put(sID, oGene);
            }
            
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    } // public void buildFromFile(InputStream isInput) {
    
    public String getID() {
        return sID;
    }
    
    public void setID(String sID) {
        if (this.sID == null) this.sID = sID;
    }
    
    public String getSpeciesID() {
        return sSpeciesID;
    }
    
    public void setSpeciesID(String sSpeciesID) {
        if (this.sSpeciesID == null) this.sSpeciesID = sSpeciesID;
    }
    
    public String getVersion() {
        return sVersion;
    }
    
    public void setVersion(String sVersion) {
        if (this.sVersion == null) this.sVersion = sVersion;
    }
    
    public Gene getGene(String sID) {
        return htGene.get(sID).clone();
    }
    
    public void setGene(Gene oGene) {
        htGene.put(oGene.sID, oGene);
    }
    
    public Gene[] getGenes() {
        Gene[] aResult;
        
        aResult = new Gene[htGene.values().size()];
        htGene.values().toArray(aResult);
        
        return aResult;
    }
    
    public void addGene(Gene oGene) {
        if (!htGene.containsKey(oGene.sID)) htGene.put(oGene.sID, oGene);
    }
    
    public void removeGene(String sID) {
        htGene.remove(sID);
    }
    
    public int getGeneCount() {
        return length();
    }
    
    public int length() {
        return this.htGene.size();
    }
    
    @Override
    public Genome clone() {
        return new Genome(this);
    }
    
    @Override
    public String toString() {
        return this.htGene.toString();
    }

}
